#include "NMEA0183.hpp"
#include "checksum.hpp"
#include "string.h"
#include <stdlib.h>
#include <stdio.h>

extern "C" size_t strnlen(const char *s, size_t maxlen);

#define TALKER_ID_LENGTH 2
#define FORMATTER_ID_LENGTH 3

using namespace NMEA0183;

/************ Parser ************/
#pragma region Parser

Parser::Parser(DataSet *dataset){
    this->dataset = dataset;
    this->error_number = 0;
    memset(this->sentence_line_buffer,0,sizeof(this->sentence_line_buffer));
    this->sentence_line_buffer_size = 0;
}

bool Parser::addSentenceParser(const char *formatter_id){
    if ( formatter_id == nullptr ){
        return false;
    }

    if ( strnlen(formatter_id,FORMATTER_ID_LENGTH) < FORMATTER_ID_LENGTH ){
        /* formatter id is too short. */
        return false;
    }

    const char *__formatter_id = nullptr;
    SentenceParser *__sentence_parser = nullptr;
    for ( int n = 0; n < this->sentence_parsers.getSize(); n++ ){
        __sentence_parser = this->sentence_parsers.get(n);
        if ( __sentence_parser == nullptr ){
            continue;
        }

        __formatter_id = __sentence_parser->getFormatterID();

        if ( __formatter_id == nullptr ){
            continue;
        }

        if ( strncmp(formatter_id,__formatter_id,FORMATTER_ID_LENGTH) == 0 ){
            /* Sentence parser with specific formatter id has been added. return true. */
            return true;
        }
    }
    __sentence_parser = createSentenceParser(formatter_id,this->dataset);

    if ( __sentence_parser == nullptr ){
        return false;
    }

    if ( !this->sentence_parsers.append(__sentence_parser) ){
        return false;
    }

    return true;
}

bool Parser::addSentenceParser(SentenceParser *sentence_parser){
    if ( sentence_parser == nullptr ){
        return false;
    }

    SentenceParser *__sentence_parser = nullptr;
    const char *formatter_id = sentence_parser->getFormatterID();
    const char *__formatter_id = nullptr;

    if ( formatter_id == nullptr ){
        /* Illegal sentence parser has nullptr formatter id. */
        return false;
    }

    for ( int n = 0; n < this->sentence_parsers.getSize(); n++ ){
        __sentence_parser = this->sentence_parsers.get(n);
        if ( __sentence_parser == nullptr ){
            continue;
        }

        if ( __sentence_parser == sentence_parser ){
            /* Sentence parser with same instance has been added. return true. */
            return true;
        }

        __formatter_id = __sentence_parser->getFormatterID();
        
        if ( __formatter_id == nullptr ){
            continue;
        }

        if ( strncmp(formatter_id,__formatter_id,FORMATTER_ID_LENGTH) == 0 ){
            /* Sentence parser with same formatter id has been added. return false. */
            return false;
        }
    }
    if ( !this->sentence_parsers.append(sentence_parser) ){
        return false;
    }
    return true;
}

bool Parser::removeSentenceParser(const char *formatter_id,bool del){
    if ( formatter_id == nullptr ){
        return false;
    }

    if ( strnlen(formatter_id,FORMATTER_ID_LENGTH) < FORMATTER_ID_LENGTH ){
        /* formatter id is too short. */
        return false;
    }

    SentenceParser *__sentence_parser = nullptr;
    const char *__formatter_id = nullptr;
    for ( int n = 0; n < this->sentence_parsers.getSize(); n++ ){
        __sentence_parser = this->sentence_parsers.get(n);

        if ( __sentence_parser == nullptr ){
            continue;
        }

        __formatter_id = __sentence_parser->getFormatterID();

        if ( __formatter_id == nullptr ){
            continue;
        }

        if ( strncmp(formatter_id,__formatter_id,FORMATTER_ID_LENGTH) == 0 ){
            /* Sentencec parser with specific formatter id was found. */
            if ( !this->sentence_parsers.remove(n) ){
                return false;
            }
            if ( del ){
                delete __sentence_parser;
            }
            return true;
        }
    }
    return false;
}

bool Parser::removeSentenceParser(SentenceParser *sentence_parser,bool del){
    if ( sentence_parser == nullptr ){
        return false;
    }

    SentenceParser *__sentence_parser = nullptr;
    for ( int n = 0; n < this->sentence_parsers.getSize(); n++ ){
        __sentence_parser = this->sentence_parsers.get(n);

        if ( __sentence_parser == nullptr ){
            continue;
        }

        if ( __sentence_parser == sentence_parser ){
            if ( !this->sentence_parsers.remove(n) ){
                return false;
            }
            if ( del ){
                delete __sentence_parser;
            }
            return true;
        }
    }
    return false;
}

SentenceParser *Parser::getSentenceParser(const char *formatter_id){
    if ( formatter_id == nullptr ){
        return nullptr;
    }

    if ( strnlen(formatter_id,FORMATTER_ID_LENGTH) < FORMATTER_ID_LENGTH ){
        /* formatter id is too short. */
        return nullptr;
    }

    SentenceParser *__sentence_parser = nullptr;
    const char *__formatter_id = nullptr;
    for ( int n = 0; n < this->sentence_parsers.getSize(); n++ ){
        __sentence_parser = this->sentence_parsers.get(n);

        if ( __sentence_parser == nullptr ){
            continue;
        }

        __formatter_id = __sentence_parser->getFormatterID();

        if ( __formatter_id == nullptr ){
            continue;
        }

        if ( strncmp(formatter_id,__formatter_id,FORMATTER_ID_LENGTH) == 0 ){
            return __sentence_parser;
        }
    }
    return nullptr;
}

bool Parser::setChecksumEnableStatus(bool status){
    this->checksum_enable_status = status;
    return true;
}

bool Parser::getChecksumEnableStatus(void){
    return this->checksum_enable_status;
}

void Parser::parse(const char *message,unsigned int length){
    char __message[length + 1];

    memset(__message,0,length + 1);
    strncpy(__message,message,length);

    for ( int n = 0; n < (length + 1); n++ ){
        if ( __message[n] == '\r' || __message[n] == '\n' ){
            __message[n] = 0;
        }
    }
    char last_chr = '\0';
    int str_length = 1;
    for ( int n = 0; n < length; n += str_length ){
        str_length = 1;
        if ( last_chr == '\0' && __message[n] != '\0' ){
            /* New line. */
            str_length = strnlen(__message + n,length - n);
            int line_length = str_length;
            char *line_header = __message + n;
            while ( *line_header == ' ' ){
                line_header ++;
                line_length --;
            }
            if ( line_header[0] != '$' ){
                continue;
            }

            /* Ignore $. */
            line_header ++;
            line_length --;

            char *content = line_header;
            unsigned int content_length = line_length;
            char *checksum = nullptr;
            for ( int m = 0; m < line_length; m++ ){
                if ( line_header[m] == '*' ){
                    line_header[m] = '\0';
                    checksum = line_header + m + 1;
                    content_length = m;
                    break;
                }
            }
            if ( this->checksum_enable_status ){
                /* check checksum. */
                char *str_next = nullptr;
                char f_checksum = 0x00;
                char r_checksum = 0x00;

                if ( checksum == nullptr ){
                    continue;
                }

                f_checksum = Crypto::checksum(line_header,content_length);
                r_checksum = strtol(checksum,&str_next,16);

                if ( f_checksum != r_checksum ){
                    printf("Check checksum failed. r(0x%02X), f(0x%02X)\r\n",r_checksum,f_checksum);
                    continue;
                }
            }
            /* Split sentence. */
            int argc = 1;
            char *argv[32];
            argv[0] = content;
            for ( int n = 0; n < content_length; n++ ){
                if ( content[n] == ',' ){
                    content[n] = '\0';
                    if ( argc >= 32 ){
                        break;
                    }
                    argv[argc++] = content + n + 1;
                }
            }
            /* Get address. */
            const char *__formatter_id = nullptr;
            for ( int index = 0; index < this->sentence_parsers.getSize(); index++ ){
                SentenceParser *sentence_parser = this->sentence_parsers.get(index);
                if ( sentence_parser == nullptr ){
                    continue;
                }
                if ( strlen(argv[0]) < 5 ){
                    continue;
                }

                __formatter_id = sentence_parser->getFormatterID();

                if ( __formatter_id == nullptr ){
                    continue;
                }
                if ( strncmp(argv[0] + 2,__formatter_id,FORMATTER_ID_LENGTH) == 0 ){
                    if ( !sentence_parser->parseArgs(argc,argv) ){
                        /* 解析失败处理. */
                    }
                    break;
                }
            }
        }
    }
    
    return;
}

void Parser::feed(const char chr){
    if ( chr == '\n' || this->sentence_line_buffer_size >= (sizeof(this->sentence_line_buffer)) ){
        /* One sentence. */
        this->parse(this->sentence_line_buffer,this->sentence_line_buffer_size);
        memset(this->sentence_line_buffer,0,sizeof(this->sentence_line_buffer));
        this->sentence_line_buffer_size = 0;
    }else{
        this->sentence_line_buffer[this->sentence_line_buffer_size++] = chr;
    }
}

int Parser::getErrorNumber(void){
    return this->error_number;
}

#pragma endregion

/************ Generator ************/
#pragma region Generator

Generator::Generator(DataSet *dataset){
    this->dataset = dataset;
    this->error_number = 0;
}

bool Generator::addSentenceGenerator(const char *formatter_id){
    if ( formatter_id == nullptr ){
        return false;
    }

    if ( strnlen(formatter_id,FORMATTER_ID_LENGTH) < FORMATTER_ID_LENGTH ){
        /* formatter id is too short. */
        return false;
    }

    const char *__formatter_id = nullptr;
    SentenceGenerator *__sentence_generator = nullptr;
    for ( int n = 0; n < this->sentence_generators.getSize(); n++ ){
        __sentence_generator = this->sentence_generators.get(n);
        if ( __sentence_generator == nullptr ){
            continue;
        }

        __formatter_id = __sentence_generator->getFormatterID();

        if ( __formatter_id == nullptr ){
            continue;
        }

        if ( strncmp(formatter_id,__formatter_id,FORMATTER_ID_LENGTH) == 0 ){
            /* Sentence generator with specific formatter id has been added. return true. */
            return true;
        }
    }

    __sentence_generator = createSentenceGenerator(formatter_id,this->dataset);

    if ( __sentence_generator == nullptr ){
        return false;
    }

    if ( !this->sentence_generators.append(__sentence_generator) ){
        return false;
    }

    return true;
}

bool Generator::addSentenceGenerator(SentenceGenerator *sentence_generator){
    if ( sentence_generator == nullptr ){
        return false;
    }

    SentenceGenerator *__sentence_generator = nullptr;
    const char *formatter_id = sentence_generator->getFormatterID();
    const char *__formatter_id = nullptr;

    if ( formatter_id == nullptr ){
        /* Illegal sentence generator has nullptr formatter id. */
        return false;
    }

    for ( int n = 0; n < this->sentence_generators.getSize(); n++ ){
        __sentence_generator = this->sentence_generators.get(n);
        if ( __sentence_generator == nullptr ){
            continue;
        }

        if ( __sentence_generator == sentence_generator ){
            /* Sentence generator with same instance has been added. return true. */
            return true;
        }

        __formatter_id = __sentence_generator->getFormatterID();

        if ( __formatter_id == nullptr ){
            continue;
        }

        if ( strncmp(formatter_id,__formatter_id,FORMATTER_ID_LENGTH) == 0 ){
            /* Sentence generator with same formatter id has been added. return false. */
            return false;
        }
    }
    if ( !this->sentence_generators.append(sentence_generator) ){
        return false;
    }
    return true;
}

bool Generator::removeSentenceGenerator(const char *formatter_id,bool del){
    if ( formatter_id == nullptr ){
        return false;
    }

    if ( strnlen(formatter_id,FORMATTER_ID_LENGTH) < FORMATTER_ID_LENGTH ){
        /* formatter id is too short. */
        return false;
    }

    SentenceGenerator *__sentence_generator = nullptr;
    const char *__formatter_id = nullptr;
    for ( int n = 0; n < this->sentence_generators.getSize(); n++ ){
        __sentence_generator = this->sentence_generators.get(n);

        if ( __sentence_generator == nullptr ){
            continue;
        }

        __formatter_id = __sentence_generator->getFormatterID();

        if ( __formatter_id == nullptr ){
            continue;
        }

        if ( strncmp(formatter_id,__formatter_id,FORMATTER_ID_LENGTH) == 0 ){
            /* Sentence generator with specific formatter id was found. */
            if ( !this->sentence_generators.remove(n) ){
                return false;
            }
            if ( del ){
                delete __sentence_generator;
            }
            return true;
        }
    }
    return false;
}

bool Generator::removeSentenceGenerator(SentenceGenerator *sentence_generator,bool del){
    if ( sentence_generator == nullptr ){
        return false;
    }

    SentenceGenerator *__sentence_generator = nullptr;
    for ( int n = 0; n < this->sentence_generators.getSize(); n++ ){
        __sentence_generator = this->sentence_generators.get(n);

        if ( __sentence_generator == nullptr ){
            continue;
        }

        if ( __sentence_generator == sentence_generator ){
            if ( !this->sentence_generators.remove(n) ){
                return false;
            }
            if ( del ){
                delete __sentence_generator;
            }
            return true;
        }
    }
    return false;
}

SentenceGenerator *Generator::getSentenceGenerator(const char *formatter_id){
    if ( formatter_id == nullptr ){
        return nullptr;
    }

    if ( strnlen(formatter_id,FORMATTER_ID_LENGTH) < FORMATTER_ID_LENGTH ){
        /* formatter id is too short. */
        return nullptr;
    }

    SentenceGenerator *__sentence_generator = nullptr;
    const char *__formatter_id = nullptr;
    for ( int n = 0; n < this->sentence_generators.getSize(); n++ ){
        __sentence_generator = this->sentence_generators.get(n);

        if ( __sentence_generator == nullptr ){
            continue;
        }

        __formatter_id = __sentence_generator->getFormatterID();

        if ( __formatter_id == nullptr ){
            continue;
        }

        if ( strncmp(formatter_id,__formatter_id,FORMATTER_ID_LENGTH) == 0 ){
            return __sentence_generator;
        }
    }
    return nullptr;
}

bool Generator::setChecksumStatus(bool status){
    this->checksum_status = status;
    return true;
}

bool Generator::getChecksumStatus(void){
    return this->checksum_status;
}

unsigned int Generator::generate(char *buffer,unsigned int max_length){
    unsigned int line_length = 0;
    unsigned int str_length = 0;
    SentenceGenerator *__sentence_generator = nullptr;
    for ( int n = 0; n < this->sentence_generators.getSize() && max_length > 0; n++ ){
        __sentence_generator = this->sentence_generators.get(n);
        if ( __sentence_generator == nullptr ){
            continue;
        }
        line_length = __sentence_generator->generate(buffer,max_length,this->checksum_status);

        max_length -= line_length;
        buffer += line_length;
        str_length += line_length;
    }
    return str_length;
}

int Generator::getErrorNumber(void){
    return this->error_number;
}

#pragma endregion

/************ Sentence Parser ************/
#pragma region Sentence Parser

SentenceParser::SentenceParser(DataSet *data_set){
    this->dataset = dataset;
    this->error_number = 0;
}

const char *SentenceParser::getFormatterID(void){
    return this->formatter_id;
}

int SentenceParser::getErrorNumber(void){
    return this->error_number;
}

#pragma endregion

/************ Sentence Generator ************/
#pragma region Sentence Generator

SentenceGenerator::SentenceGenerator(DataSet *dataset){
    this->dataset = dataset;
    this->error_number = 0;
}

const char *SentenceGenerator::getFormatterID(void){
    return this->formatter_id;
}

bool SentenceGenerator::setTalkerID(const char *talker_id){
    if ( talker_id == nullptr ){
        return false;
    }

    if ( strnlen(talker_id,TALKER_ID_LENGTH) < TALKER_ID_LENGTH ){
        return false;
    }

    talker_id = talker_id;

    return true;
}

int SentenceGenerator::getErrorNumber(void){
    return this->error_number;
}

#pragma endregion

/************ Sentence Parsers ************/
#pragma region Sentence Parsers

class GNSSentenceParser : public SentenceParser {
public:
    explicit GNSSentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "GNS";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 13 ){
            /* The arguments of GNS sentence should not lesser than 13. */
            return false;
        }

        int utc_hour = 0;
        int utc_minute = 0;
        int utc_second = 0;
        double latitude = 0.0;
        double longitude = 0.0;
        char str_buffer[32];
        /* Ignore other argument. */

        /* Process UTC time. */
        if ( strnlen(argv[1],9) >= 9 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],2);
            utc_hour = atoi(str_buffer);
            strncpy(str_buffer,argv[1] + 2,2);
            utc_minute = atoi(str_buffer);
            strncpy(str_buffer,argv[1] + 4,2);
            utc_second = atoi(str_buffer);
            /* Update dataset. */
            this->dataset->set("UTC.Hour",(double)utc_hour);
            this->dataset->set("UTC.Minute",(double)utc_minute);
            this->dataset->set("UTC.Second",(double)utc_second);
        }

        /* Process Latitude. */
        if ( strnlen(argv[2],7) >= 7 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[2],2);
            latitude = (double)atoi(str_buffer);
            strncpy(str_buffer,argv[2] + 2,5);
            latitude += atof(str_buffer) / 60.0;
            if ( strncmp(argv[3],"S",1) == 0 ){
                latitude = -latitude;
            }
            /* Update dataset. */
            this->dataset->set("Latitude",latitude);
        }

        /* Process Longitude. */
        if ( strnlen(argv[4],8) >= 8 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[4],3);
            longitude = (double)atoi(str_buffer);
            strncpy(str_buffer,argv[4] + 3,5);
            longitude += atof(str_buffer) / 60.0;
            if ( strncmp(argv[5],"W",1) == 0 ){
                longitude = -longitude;
            }
            /* Update dataset. */
            this->dataset->set("Longitude",longitude);
        }

        return true;
    }
};

class GGASentenceParser : public SentenceParser {
public:
    explicit GGASentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "GGA";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 14 ){
            /* The arguments of GGA sentence should not lesser than 14. */
            return false;
        }

        int utc_hour = 0;
        int utc_minute = 0;
        int utc_second = 0;
        double latitude = 0.0;
        double longitude = 0.0;
        char str_buffer[32];
        /* Ignore other argument. */

        /* Process UTC time. */
        if ( strnlen(argv[1],9) >= 9 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],2);
            utc_hour = atoi(str_buffer);
            strncpy(str_buffer,argv[1] + 2,2);
            utc_minute = atoi(str_buffer);
            strncpy(str_buffer,argv[1] + 4,2);
            utc_second = atoi(str_buffer);
            /* Update dataset. */
            this->dataset->set("UTC.Hour",(double)utc_hour);
            this->dataset->set("UTC.Minute",(double)utc_minute);
            this->dataset->set("UTC.Second",(double)utc_second);
        }

        /* Process Latitude. */
        if ( strnlen(argv[2],7) >= 7 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[2],2);
            latitude = (double)atoi(str_buffer);
            strncpy(str_buffer,argv[2] + 2,5);
            latitude += atof(str_buffer) / 60.0;
            if ( strncmp(argv[3],"S",1) == 0 ){
                latitude = -latitude;
            }
            /* Update dataset. */
            this->dataset->set("Latitude",latitude);
        }

        /* Process Longitude. */
        if ( strnlen(argv[4],8) >= 8 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[4],3);
            longitude = (double)atoi(str_buffer);
            strncpy(str_buffer,argv[4] + 3,5);
            longitude += atof(str_buffer) / 60.0;
            if ( strncmp(argv[5],"W",1) == 0 ){
                longitude = -longitude;
            }
            /* Update dataset. */
            this->dataset->set("Longitude",longitude);
        }

        return true;
    }
};

class RMCSentenceParser : public SentenceParser {
public:
    explicit RMCSentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "RMC";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 13 ){
            /* The arguments of RMC sentence should not lesser than 13. */
            return false;
        }

        int utc_hour = 0;
        int utc_minute = 0;
        int utc_second = 0;
        double latitude = 0.0;
        double longitude = 0.0;
        char str_buffer[32];
        double SOG_kn = 0.0;
        double COG_true = 0.0;
        int utc_year = 0;
        int utc_month = 0;
        int utc_date = 0;
        /* Ignore other argument. */

        /* Process UTC time. */
        if ( strnlen(argv[1],9) >= 9 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],2);
            utc_hour = atoi(str_buffer);
            strncpy(str_buffer,argv[1] + 2,2);
            utc_minute = atoi(str_buffer);
            strncpy(str_buffer,argv[1] + 4,2);
            utc_second = atoi(str_buffer);
            /* Update dataset. */
            this->dataset->set("UTC.Hour",(double)utc_hour);
            this->dataset->set("UTC.Minute",(double)utc_minute);
            this->dataset->set("UTC.Second",(double)utc_second);
        }

        /* Process Latitude. */
        if ( strnlen(argv[3],7) >= 7 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[3],2);
            latitude = (double)atoi(str_buffer);
            strncpy(str_buffer,argv[3] + 2,5);
            latitude += atof(str_buffer) / 60.0;
            if ( strncmp(argv[4],"S",1) == 0 ){
                latitude = -latitude;
            }
            /* Update dataset. */
            this->dataset->set("Latitude",latitude);
        }

        /* Process Longitude. */
        if ( strnlen(argv[5],8) >= 8 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[5],3);
            longitude = (double)atoi(str_buffer);
            strncpy(str_buffer,argv[5] + 3,5);
            longitude += atof(str_buffer) / 60.0;
            if ( strncmp(argv[6],"W",1) == 0 ){
                longitude = -longitude;
            }
            /* Update dataset. */
            this->dataset->set("Longitude",longitude);
        }

        /* Process SOG kn. */
        if ( strnlen(argv[7],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[7],32);
            SOG_kn = (double)atof(str_buffer);
            /* Update dataset. */
            this->dataset->set("SOG.Kn",SOG_kn);
        }

        /* Process COG true. */
        if ( strnlen(argv[8],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[8],32);
            COG_true = (double)atof(str_buffer);
            /* Update dataset. */
            this->dataset->set("COG.True",COG_true);
        }

        /* Process UTC YY-MM-DD. */
        if ( strnlen(argv[9],6) >= 6 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[9],2);
            utc_date = atoi(str_buffer);
            strncpy(str_buffer,argv[9] + 2,2);
            utc_month = atoi(str_buffer);
            strncpy(str_buffer,argv[9] + 4,2);
            int temp = atoi(str_buffer);
            utc_year = (int)dataset->getNumber("UTC.Year");
            utc_year -= (utc_year % 100);
            utc_year += temp % 100;
            /* Update dataset. */
            this->dataset->set("UTC.Year",(double)utc_year);
            this->dataset->set("UTC.Month",(double)utc_month);
            this->dataset->set("UTC.Date",(double)utc_date);
        }

        return true;
    }
};

class GLLSentenceParser : public SentenceParser {
public:
    explicit GLLSentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "GLL";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 8 ){
            /* The arguments of GLL sentence should not lesser than 8. */
            return false;
        }
        
        int utc_hour = 0;
        int utc_minute = 0;
        int utc_second = 0;
        double latitude = 0.0;
        double longitude = 0.0;
        char str_buffer[32];
        /* Ignore other argument. */

        /* Process Latitude. */
        if ( strnlen(argv[1],7) >= 7 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],2);
            latitude = (double)atoi(str_buffer);
            strncpy(str_buffer,argv[1] + 2,5);
            latitude += atof(str_buffer) / 60.0;
            if ( strncmp(argv[2],"S",1) == 0 ){
                latitude = -latitude;
            }
            /* Update dataset. */
            this->dataset->set("Latitude",latitude);
        }

        /* Process Longitude. */
        if ( strnlen(argv[3],8) >= 8 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[3],3);
            longitude = (double)atoi(str_buffer);
            strncpy(str_buffer,argv[3] + 3,5);
            longitude += atof(str_buffer) / 60.0;
            if ( strncmp(argv[4],"W",1) == 0 ){
                longitude = -longitude;
            }
            /* Update dataset. */
            this->dataset->set("Longitude",longitude);
        }

        /* Process UTC time. */
        if ( strnlen(argv[5],9) >= 9 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[5],2);
            utc_hour = atoi(str_buffer);
            strncpy(str_buffer,argv[5] + 2,2);
            utc_minute = atoi(str_buffer);
            strncpy(str_buffer,argv[5] + 4,2);
            utc_second = atoi(str_buffer);
            /* Update dataset. */
            this->dataset->set("UTC.Hour",(double)utc_hour);
            this->dataset->set("UTC.Minute",(double)utc_minute);
            this->dataset->set("UTC.Second",(double)utc_second);
        }

        return true;
    }
};

class VTGSentenceParser : public SentenceParser {
public:
    explicit VTGSentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "VTG";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 10 ){
            /* The arguments of VTG sentence should not lesser than 10. */
            return false;
        }
        double COG_true = 0.0;
        double COG_magnetic = 0.0;
        double SOG_kn = 0.0;
        double SOG_kph = 0.0;
        char str_buffer[32];
        /* Ignore other argument. */

        /* Process COG true. */
        if ( strnlen(argv[1],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],32);
            COG_true = atof(str_buffer);
            /* Update dataset. */
            this->dataset->set("COG.True",COG_true);
        }

        /* Process COG Magnetic. */
        if ( strnlen(argv[3],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[3],32);
            COG_magnetic = atof(str_buffer);
            /* Update dataset. */
            this->dataset->set("COG.Magnetic",COG_magnetic);
        }

        /* Process SOG kn. */
        if ( strnlen(argv[5],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[5],32);
            SOG_kn = atof(str_buffer);
            /* Update dataset. */
            this->dataset->set("SOG.Kn",SOG_kn);
        }

        /* Process SOG kph. */
        if ( strnlen(argv[7],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[7],32);
            SOG_kph = atof(str_buffer);
            /* Update dataset. */
            this->dataset->set("SOG.Kph",SOG_kph);
        }

        return true;
    }
};

class ZDASentenceParser : public SentenceParser {
public:
    explicit ZDASentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "ZDA";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 7 ){
            /* The arguments of ZDA sentence should not lesser than 7. */
            return false;
        }
        int utc_year = 0;
        int utc_month = 0;
        int utc_date = 0;
        int utc_hour = 0;
        int utc_minute = 0;
        int utc_second = 0;
        char str_buffer[32];
        /* Ignore other argument. */

        /* Process UTC hh:mm:ss. */
        if ( strnlen(argv[1],9) >= 9 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],2);
            utc_hour = atoi(str_buffer);
            strncpy(str_buffer,argv[1] + 2,2);
            utc_minute = atoi(str_buffer);
            strncpy(str_buffer,argv[1] + 4,2);
            utc_second = atoi(str_buffer);
            /* Update dataset. */
            this->dataset->set("UTC.Hour",(double)utc_hour);
            this->dataset->set("UTC.Minute",(double)utc_minute);
            this->dataset->set("UTC.Second",(double)utc_second);
        }

        /* Process UTC date. */
        if ( strnlen(argv[2],2) >= 2 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[2],2);
            utc_date = atoi(str_buffer);
            /* Update dataset. */
            this->dataset->set("UTC.Date",(double)utc_date);
        }

        /* Process UTC Month. */
        if ( strnlen(argv[3],2) >= 2 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[3],2);
            utc_month = atoi(str_buffer);
            /* Update dataset. */
            this->dataset->set("UTC.Month",(double)utc_month);
        }

        /* Process UTC Year. */
        if ( strnlen(argv[4],4) >= 4 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[4],4);
            utc_year = atoi(str_buffer);
            /* Update dataset. */
            this->dataset->set("UTC.Year",(double)utc_year);
        }

        return true;
    }
};

class HDTSentenceParser : public SentenceParser {
public:
    explicit HDTSentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "HDT";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 3 ){
            /* The arguments of HDT sentence should not lesser than 3. */
            return false;
        }
        double heading_true = 0.0;
        char str_buffer[32];

        /* Process Heading true. */
        if ( strnlen(argv[1],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],32);
            heading_true = atof(str_buffer);
            /* Update dataset. */
            this->dataset->set("Heading.True",heading_true);
        }

        return true;
    }
};

class HDGSentenceParser : public SentenceParser {
public:
    explicit HDGSentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "HDG";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 6 ){
            /* The arguments of HDG sentence should not lesser than 6. */
            return false;
        }
        double heading_magnetic = 0.0;
        char str_buffer[32];
        /* Ignore other argument. */

        /* Process heading magnetic. */
        if ( strnlen(argv[1],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],32);
            heading_magnetic = atof(str_buffer);
            /* Update dataset. */
            this->dataset->set("Heading.Magnetic",heading_magnetic);
        }

        return true;
    }
};

class HDMSentenceParser : public SentenceParser {
public:
    explicit HDMSentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "HDM";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 3 ){
            /* The arguments of HDM sentence should not lesser than 3. */
            return false;
        }
        double heading_magnetic = 0.0;
        char str_buffer[32];

        /* Process heading magnetic. */
        if ( strnlen(argv[1],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],32);
            heading_magnetic = atof(str_buffer);
            /* Update dataset. */
            this->dataset->set("Heading.Magnetic",heading_magnetic);
        }

        return true;
    }
};

class ROTSentenceParser : public SentenceParser {
public:
    explicit ROTSentenceParser(DataSet *dataset) : SentenceParser(dataset){
        this->dataset = dataset;
        this->formatter_id = "ROT";
    }

    virtual bool parseArgs(int argc,char *argv[]){
        if ( this->dataset == nullptr ){
            return false;
        }
        if ( argc < 3 ){
            /* The arguments of ROT sentence should not lesser than 3. */
            return false;
        }
        double ROT = 0.0;
        char str_buffer[32];

        /* Process ROT. */
        if ( strnlen(argv[1],3) >= 3 ){
            memset(str_buffer,0,sizeof(str_buffer));
            strncpy(str_buffer,argv[1],32);
            ROT = atof(str_buffer);
            /* Update dataset. */
            dataset->set("ROT",ROT);
        }

        return true;
    }
};

#pragma endregion

/************ Sentence Generators ************/
#pragma region Sentence Generators

class GGASentenceGenerator : public SentenceGenerator {
public:
    explicit GGASentenceGenerator(DataSet *dataset) : SentenceGenerator(dataset){
        this->dataset = dataset;
        this->talker_id = "GP";
        this->formatter_id = "GGA";
    }

    virtual unsigned int generate(char *buffer,unsigned int max_length,bool checksum_status){
        if ( buffer == nullptr || this->dataset == nullptr || max_length == 0 ){
            return 0;
        }

        double latitude = 0.0;
        char latitude_ns = 'N';
        double longitude = 0.0;
        char longitude_ew = 'E';
        int utc_hour = 0;
        int utc_minute = 0;
        int utc_second = 0;
        char checksum = 0x00;

        int latitude_degree = 0;
        int latitude_minute_tens = 0;
        int latitude_minute_frac = 0;
        int longitude_degree = 0;
        int longitude_minute_tens = 0;
        int longitude_minute_frac = 0;
        int temp = 0;
        char str_buffer[128] = {0};

        /* Load data from dataset. */
        latitude = dataset->getNumber("Latitude");
        longitude = dataset->getNumber("Longitude");
        utc_hour = (int)dataset->getNumber("UTC.Hour");
        utc_minute = (int)dataset->getNumber("UTC.Minute");
        utc_second = (int)dataset->getNumber("UTC.Second");

        /* Process data. */
        if ( latitude < 0.0 ){
            latitude_ns = 'S';
            latitude = -latitude;
        }else{
            latitude_ns = 'N';
        }

        if ( longitude < 0.0 ){
            longitude_ew = 'W';
            longitude = -longitude;
        }else{
            longitude_ew = 'E';
        }

        latitude *= 6000;
        temp = (int)latitude;
        latitude_degree = temp / 6000;
        temp = temp % 6000;
        latitude_minute_tens = temp / 100;
        latitude_minute_frac = temp % 100;

        longitude *= 6000;
        temp = (int)longitude;
        longitude_degree = temp / 6000;
        temp = temp % 6000;
        longitude_minute_tens = temp / 100;
        longitude_minute_frac = temp % 100;

        /* Format string. */
        int size = snprintf(str_buffer,128,"$%2s%3s,%02d%02d%02d.00,%02d%02d.%02d,%c,%03d%02d.%02d,%c,2,06,1.9,20.0,M,0.0,M,7.3,0034",
            this->talker_id,
            this->formatter_id,
            utc_hour,
            utc_minute,
            utc_second,
            latitude_degree,
            latitude_minute_tens,
            latitude_minute_frac,
            latitude_ns,
            longitude_degree,
            longitude_minute_tens,
            longitude_minute_frac,
            longitude_ew
        );

        if ( checksum_status ){
            checksum = Crypto::checksum(str_buffer,size);
            size = snprintf(buffer,max_length,"%s*%02X\r\n",str_buffer,checksum);
        }else{
            size = snprintf(buffer,max_length,"%s\r\n",str_buffer);
        }
        
        return (unsigned int)size;
    }
};

class RMCSentenceGenerator : public SentenceGenerator {
public:
    explicit RMCSentenceGenerator(DataSet *dataset) : SentenceGenerator(dataset){
        this->dataset = dataset;
        this->talker_id = "GP";
        this->formatter_id = "RMC";
    }

    virtual unsigned int generate(char *buffer,unsigned int max_length,bool checksum_status){
        if ( buffer == nullptr || this->dataset == nullptr || max_length == 0 ){
            return 0;
        }

        double latitude = 0.0;
        char latitude_ns = 'N';
        double longitude = 0.0;
        char longitude_ew = 'E';
        int utc_hour = 0;
        int utc_minute = 0;
        int utc_second = 0;
        int utc_year = 0;
        int utc_month = 0;
        int utc_date = 0;
        char checksum = 0x00;
        double COG_true = 0.0;
        double SOG_kn;

        int latitude_degree = 0;
        int latitude_minute_tens = 0;
        int latitude_minute_frac = 0;
        int longitude_degree = 0;
        int longitude_minute_tens = 0;
        int longitude_minute_frac = 0;
        int temp = 0;
        char str_buffer[128] = {0};

        /* Load data from dataset. */
        latitude = dataset->getNumber("Latitude");
        longitude = dataset->getNumber("Longitude");
        utc_hour = (int)dataset->getNumber("UTC.Hour");
        utc_minute = (int)dataset->getNumber("UTC.Minute");
        utc_second = (int)dataset->getNumber("UTC.Second");
        utc_year = (int)dataset->getNumber("UTC.Year");
        utc_month = (int)dataset->getNumber("UTC.Month");
        utc_date = (int)dataset->getNumber("UTC.Date");
        COG_true = dataset->getNumber("COG.True");
        SOG_kn = dataset->getNumber("SOG.Kn");

        /* Process data. */
        if ( latitude < 0.0 ){
            latitude_ns = 'S';
            latitude = -latitude;
        }else{
            latitude_ns = 'N';
        }

        if ( longitude < 0.0 ){
            longitude_ew = 'W';
            longitude = -longitude;
        }else{
            longitude_ew = 'E';
        }

        latitude *= 6000;
        temp = (int)latitude;
        latitude_degree = temp / 6000;
        temp = temp % 6000;
        latitude_minute_tens = temp / 100;
        latitude_minute_frac = temp % 100;

        longitude *= 6000;
        temp = (int)longitude;
        longitude_degree = temp / 6000;
        temp = temp % 6000;
        longitude_minute_tens = temp / 100;
        longitude_minute_frac = temp % 100;

        /* Format string. */
        int size = snprintf(str_buffer,128,"$%2s%3s,%02d%02d%02d.00,A,%02d%02d.%02d,%c,%03d%02d.%02d,%c,%.1f,%.1f,%02d%02d%02d,0.0,E,D",
            this->talker_id,
            this->formatter_id,
            utc_hour,
            utc_minute,
            utc_second,
            latitude_degree,
            latitude_minute_tens,
            latitude_minute_frac,
            latitude_ns,
            longitude_degree,
            longitude_minute_tens,
            longitude_minute_frac,
            longitude_ew,
            SOG_kn,
            COG_true,
            utc_year,
            utc_month,
            utc_date
        );

        if ( checksum_status ){
            checksum = Crypto::checksum(str_buffer,size);
            size = snprintf(buffer,max_length,"%s*%02X\r\n",str_buffer,checksum);
        }else{
            size = snprintf(buffer,max_length,"%s\r\n",str_buffer);
        }

        return (unsigned int)size;
    }
};

class GLLSentenceGenerator : public SentenceGenerator {
public:
    explicit GLLSentenceGenerator(DataSet *dataset) : SentenceGenerator(dataset){
        this->dataset = dataset;
        this->talker_id = "GP";
        this->formatter_id = "GLL";
    }

    virtual unsigned int generate(char *buffer,unsigned int max_length,bool checksum_status){
        if ( buffer == nullptr || this->dataset == nullptr || max_length == 0 ){
            return 0;
        }

        double latitude = 0.0;
        char latitude_ns = 'N';
        double longitude = 0.0;
        char longitude_ew = 'E';
        int utc_hour = 0;
        int utc_minute = 0;
        int utc_second = 0;
        int latitude_degree = 0;
        int latitude_minute_tens = 0;
        int latitude_minute_frac = 0;
        int longitude_degree = 0;
        int longitude_minute_tens = 0;
        int longitude_minute_frac = 0;
        int temp = 0;
        char checksum = 0x00;
        char str_buffer[128] = {0};

        /* Load data from dataset. */
        latitude = dataset->getNumber("Latitude");
        longitude = dataset->getNumber("Longitude");
        utc_hour = (int)dataset->getNumber("UTC.Hour");
        utc_minute = (int)dataset->getNumber("UTC.Minute");
        utc_second = (int)dataset->getNumber("UTC.Second");

        /* Process data. */
        if ( latitude < 0.0 ){
            latitude_ns = 'S';
            latitude = -latitude;
        }else{
            latitude_ns = 'N';
        }

        if ( longitude < 0.0 ){
            longitude_ew = 'W';
            longitude = -longitude;
        }else{
            longitude_ew = 'E';
        }

        latitude *= 6000;
        temp = (int)latitude;
        latitude_degree = temp / 6000;
        temp = temp % 6000;
        latitude_minute_tens = temp / 100;
        latitude_minute_frac = temp % 100;

        longitude *= 6000;
        temp = (int)longitude;
        longitude_degree = temp / 6000;
        temp = temp % 6000;
        longitude_minute_tens = temp / 100;
        longitude_minute_frac = temp % 100;

        /* Format string. */
        int size = snprintf(str_buffer,128,"$%2s%3s,%02d%02d.%02d,%c,%03d%02d.%02d,%c,%02d%02d%02d.00,A,D",
            this->talker_id,
            this->formatter_id,
            latitude_degree,
            latitude_minute_tens,
            latitude_minute_frac,
            latitude_ns,
            longitude_degree,
            longitude_minute_tens,
            longitude_minute_frac,
            longitude_ew,
            utc_hour,
            utc_minute,
            utc_second
        );

        if ( checksum_status ){
            checksum = Crypto::checksum(str_buffer,size);
            size = snprintf(buffer,max_length,"%s*%02X\r\n",str_buffer,checksum);
        }else{
            size = snprintf(buffer,max_length,"%s\r\n",str_buffer);
        }

        return (unsigned int)size;
    }
};

class VTGSentenceGenerator : public SentenceGenerator {
public:
    explicit VTGSentenceGenerator(DataSet *dataset) : SentenceGenerator(dataset){
        this->dataset = dataset;
        this->talker_id = "GP";
        this->formatter_id = "VTG";
    }

    virtual unsigned int generate(char *buffer,unsigned int max_length,bool checksum_status){
        if ( buffer == nullptr || this->dataset == nullptr || max_length == 0 ){
            return 0;
        }

        double COG_true = 0.0;
        double COG_magnetic = 0.0;
        double SOG_kn = 0.0;
        double SOG_kph = 0.0;
        char checksum = 0x00;
        char str_buffer[128] = {0};

        /* Load data from dataset. */
        COG_true = dataset->getNumber("COG.True");
        COG_magnetic = dataset->getNumber("COG.Magnetic");
        SOG_kn = dataset->getNumber("SOG.Kn");
        SOG_kph = dataset->getNumber("SOG.Kph");

        /* Format string. */
        int size = snprintf(str_buffer,128,"$%2s%3s,%.1f,T,%.1f,M,%.1f,N,%.1f,K,D",
            this->talker_id,
            this->formatter_id,
            COG_true,
            COG_magnetic,
            SOG_kn,
            SOG_kph
        );

        if ( checksum_status ){
            checksum = Crypto::checksum(str_buffer,size);
            size = snprintf(buffer,max_length,"%s*%02X\r\n",str_buffer,checksum);
        }else{
            size = snprintf(buffer,max_length,"%s\r\n",str_buffer);
        }

        return (unsigned int)size;
    }
};

class ZDASentenceGenerator : public SentenceGenerator {
public:
    explicit ZDASentenceGenerator(DataSet *dataset) : SentenceGenerator(dataset){
        this->dataset = dataset;
        this->talker_id = "GP";
        this->formatter_id = "ZDA";
    }

    virtual unsigned int generate(char *buffer,unsigned int max_length,bool checksum_status){
        if ( buffer == nullptr || this->dataset == nullptr || max_length == 0 ){
            return 0;
        }

        int utc_year = 0;
        int utc_month = 0;
        int utc_date = 0;
        int utc_hour = 0;
        int utc_minute = 0;
        int utc_second = 0;
        char checksum = 0x00;
        char str_buffer[128] = {0};

        /* Load data from dataset. */
        utc_year = (int)dataset->getNumber("UTC.Year");
        utc_month = (int)dataset->getNumber("UTC.Month");
        utc_date = (int)dataset->getNumber("UTC.Date");
        utc_hour = (int)dataset->getNumber("UTC.Hour");
        utc_minute = (int)dataset->getNumber("UTC.Minute");
        utc_second = (int)dataset->getNumber("UTC.Second");

        /* Format string. */
        int size = snprintf(str_buffer,128,"$%2s%3s,%02d%02d%02d.00,%02d,%02d,%04d,00,00",
            this->talker_id,
            this->formatter_id,
            utc_hour,
            utc_minute,
            utc_second,
            utc_date,
            utc_month,
            utc_year
        );

        if ( checksum_status ){
            checksum = Crypto::checksum(str_buffer,size);
            size = snprintf(buffer,max_length,"%s*%02X\r\n",str_buffer,checksum);
        }else{
            size = snprintf(buffer,max_length,"%s\r\n",str_buffer);
        }

        return (unsigned int)size;
    }
};

class HDTSentenceGenerator : public SentenceGenerator {
public:
    explicit HDTSentenceGenerator(DataSet *dataset) : SentenceGenerator(dataset){
        this->dataset = dataset;
        this->talker_id = "TI";
        this->formatter_id = "HDT";
    }

    virtual unsigned int generate(char *buffer,unsigned int max_length,bool checksum_status){
        if ( buffer == nullptr || this->dataset == nullptr || max_length == 0 ){
            return 0;
        }

        double heading_true = 0.0;
        char checksum = 0x00;
        char str_buffer[128] = {0};

        /* Load data from dataset. */
        heading_true = dataset->getNumber("Heading.True");

        /* Format string. */
        int size = snprintf(str_buffer,128,"$%2s%3s,%.1f,T",
            this->talker_id,
            this->formatter_id,
            heading_true
        );

        if ( checksum_status ){
            checksum = Crypto::checksum(str_buffer,size);
            size = snprintf(buffer,max_length,"%s*%02X\r\n",str_buffer,checksum);
        }else{
            size = snprintf(buffer,max_length,"%s\r\n",str_buffer);
        }

        return (unsigned int)size;
    }
};

class HDGSentenceGenerator : public SentenceGenerator {
public:
    explicit HDGSentenceGenerator(DataSet *dataset) : SentenceGenerator(dataset){
        this->dataset = dataset;
        this->talker_id = "TI";
        this->formatter_id = "HDG";
    }

    virtual unsigned int generate(char *buffer,unsigned int max_length,bool checksum_status){
        if ( buffer == nullptr || this->dataset == nullptr || max_length == 0 ){
            return 0;
        }

        double heading_magnetic = 0.0;
        char checksum = 0x00;
        char str_buffer[128] = {0};

        /* Load data from dataset. */
        heading_magnetic = dataset->getNumber("Heading.Magnetic");

        /* Format string. */
        int size = snprintf(str_buffer,128,"$%2s%3s,%.1f,0.0,E,0.0,E",
            this->talker_id,
            this->formatter_id,
            heading_magnetic
        );

        if ( checksum_status ){
            checksum = Crypto::checksum(str_buffer,size);
            size = snprintf(buffer,max_length,"%s*%02X\r\n",str_buffer,checksum);
        }else{
            size = snprintf(buffer,max_length,"%s\r\n",str_buffer);
        }

        return (unsigned int)size;
    }
};

class HDMSentenceGenerator : public SentenceGenerator {
public:
    explicit HDMSentenceGenerator(DataSet *dataset) : SentenceGenerator(dataset){
        this->dataset = dataset;
        this->talker_id = "TI";
        this->formatter_id = "HDM";
    }

    virtual unsigned int generate(char *buffer,unsigned int max_length,bool checksum_status){
        if ( buffer == nullptr || this->dataset == nullptr || max_length == 0 ){
            return 0;
        }

        double heading_magnetic = 0.0;
        char checksum = 0x00;
        char str_buffer[128] = {0};

        /* Load data from dataset. */
        heading_magnetic = dataset->getNumber("Heading.True");

        /* Format string. */
        int size = snprintf(str_buffer,128,"$%2s%3s,%.1f,M",
            this->talker_id,
            this->formatter_id,
            heading_magnetic
        );

        if ( checksum_status ){
            checksum = Crypto::checksum(str_buffer,size);
            size = snprintf(buffer,max_length,"%s*%02X\r\n",str_buffer,checksum);
        }else{
            size = snprintf(buffer,max_length,"%s\r\n",str_buffer);
        }

        return (unsigned int)size;
    }
};

class ROTSentenceGenerator : public SentenceGenerator {
public:
    explicit ROTSentenceGenerator(DataSet *dataset) : SentenceGenerator(dataset){
        this->dataset = dataset;
        this->talker_id = "TI";
        this->formatter_id = "ROT";
    }

    virtual unsigned int generate(char *buffer,unsigned int max_length,bool checksum_status){
        if ( buffer == nullptr || this->dataset == nullptr || max_length == 0 ){
            return 0;
        }

        double ROT = 0.0;
        char checksum = 0x00;
        char str_buffer[128] = {0};
        
        /* Load data from dataset. */
        ROT = dataset->getNumber("ROT");

        /* Format string. */
        int size = snprintf(str_buffer,128,"$%2s%3s,%.1f,A",
            this->talker_id,
            this->formatter_id,
            ROT
        );

        if ( checksum_status ){
            checksum = Crypto::checksum(str_buffer,size);
            size = snprintf(buffer,max_length,"%s*%02X\r\n",str_buffer,checksum);
        }else{
            size = snprintf(buffer,max_length,"%s\r\n",str_buffer);
        }

        return (unsigned int)size;
    }
};

#pragma endregion

/************ Global Functions ************/
#pragma region Global Functions

SentenceParser *NMEA0183::createSentenceParser(const char *formatter_id,DataSet *dataset){
    return nullptr;
}

SentenceGenerator *NMEA0183::createSentenceGenerator(const char *formatter_id,DataSet *dataset){
    return nullptr;
}

#pragma endregion